#pragma once

//Suport ASSERT & CS
#include <atldef.h>
#include <atlcore.h>
#include <atlbase.h>

#include <stdlib.h>
#include <string>
#include <vector>

#include "curl/curl.h"
#pragma comment(lib, "curllib.lib")

#include "HttpStreamHandler.h"

using std::string;
using std::vector;

//500 Chars of max url
#define _CURL_MAX_URL_LENGTH		500

//Method Type define
typedef size_t (*_CURL_WRITE_DATA_FUNCTION)(void*, size_t, size_t, void*);

class CCURLHelper
{
//Type define
public:
	//Unknown size file buffered download struct
	typedef struct _BUFFER_DOWNFILE {
		HANDLE hFile;
		char* pcBuffer;

		UINT uiFileReportSize;
		UINT uiFileWrittenSize;
		UINT uiFileVirtualSize;

		UINT uiCurrentBufferedSize;
		UINT uiBufferCapicity;
		UINT uiBufferThreshold;

		LARGE_INTEGER lintFileTotalOffset;
		LARGE_INTEGER lintVirtualSteppingSize;

		//Default Constructor
		_BUFFER_DOWNFILE(HANDLE hFileHandle, UINT uiRepFileSize = 0) 
			: hFile(NULL), pcBuffer(NULL), uiFileReportSize(uiRepFileSize), uiFileWrittenSize(0), uiFileVirtualSize(0), uiCurrentBufferedSize(0), 
			uiBufferCapicity(sm_uiDownBufferSize), uiBufferThreshold(sm_uiDownBufferThreshold)
		{
			ATLASSERT(hFileHandle != NULL);
			
			hFile = hFileHandle;
			pcBuffer = new char[uiBufferCapicity];

			lintFileTotalOffset.QuadPart = uiFileReportSize;
			lintVirtualSteppingSize.QuadPart = uiBufferCapicity * 2;
		}

		void FlushingBufferToFile()
		{
			if(uiCurrentBufferedSize > 0)
			{
				DWORD	dwWritten;

				//if buffer bigger than file size, resize to final file
				if((uiFileWrittenSize + uiCurrentBufferedSize) > uiFileVirtualSize)
				{
					LARGE_INTEGER liPreFilePos;
					LARGE_INTEGER liTempNewPos;
					LARGE_INTEGER liOffToEnd;

					//Init
					liOffToEnd.QuadPart = 0;
					liOffToEnd.LowPart += ((uiFileWrittenSize + uiCurrentBufferedSize) - uiFileVirtualSize);

					//Save Current Pos
					::SetFilePointerEx(hFile, LINT_ZERO, &liPreFilePos, FILE_CURRENT);

					//Enlarge file
					::SetFilePointerEx(hFile, liOffToEnd, &liTempNewPos, FILE_END);
					::SetEndOfFile(hFile);

					uiFileVirtualSize += liOffToEnd.LowPart;

					//Rewind to last pos
					::SetFilePointerEx(hFile, liPreFilePos, &liTempNewPos, FILE_BEGIN);
				}

				//Write buffer to file, and reset the buffersize
				::WriteFile(hFile, pcBuffer, uiCurrentBufferedSize, &dwWritten, NULL);
				uiFileWrittenSize += dwWritten;
				uiCurrentBufferedSize = 0;

				printf("\tWrite Final Data.....\n");
			}
			//this always happend when the file size is smaller than buffer
			//Or the last transfer just written to buffer
			if(uiFileVirtualSize > uiFileWrittenSize)
			{
				LARGE_INTEGER liRealFileSize; liRealFileSize.QuadPart = uiFileWrittenSize;
				LARGE_INTEGER liTempNewPos;

				::SetFilePointerEx(hFile, liRealFileSize, &liTempNewPos, FILE_BEGIN);
				::SetEndOfFile(hFile);
				uiFileVirtualSize = uiFileWrittenSize;
			}

			::CloseHandle(hFile);
		}

	} BUFFER_DOWNFILE;

	//Helper data struct for part download, each thread have a instance of it
	typedef struct _PART_DOWNLOAD {

		SHORT sID;							//File Part ID
		HANDLE hEntireFileHandle;			//Entire File Handle
		char* pcBuffer;						//Download buffer
		bool bIsFinished;					//If this part finished downloading

		UINT uiFileWrittenPartSize;			//Part written size
		UINT uiFileVirtualPartSize;			//Part total virtual size, init in the thread function, cause we known the part size that time

		UINT uiCurrentBufferedSize;			//Buffered download which not written to file
		UINT uiBufferCapicity;				//The total buffersize
		UINT uiBufferThreshold;				//Threadhold for buffer writting

		LARGE_INTEGER lintPartStartPos;		//Part file start position
		LARGE_INTEGER lintPartEndPos;		//Part file end position

		//Default Constructor
		_PART_DOWNLOAD(HANDLE hFileHandle, UINT uiPartBegin = 0, UINT uiPartEnd = 0, SHORT sPartID = -1) 
			: hEntireFileHandle(NULL), pcBuffer(NULL), sID(sPartID), uiFileWrittenPartSize(0), uiFileVirtualPartSize(uiPartEnd-uiPartBegin), uiCurrentBufferedSize(0), bIsFinished(false), 
			uiBufferCapicity(sm_uiDownBufferSize), uiBufferThreshold(sm_uiDownBufferThreshold)
		{
			ATLASSERT(hFileHandle != NULL);
			
			hEntireFileHandle = hFileHandle;
			pcBuffer = new char[uiBufferCapicity];

			lintPartStartPos.QuadPart = uiPartBegin;
			lintPartEndPos.QuadPart = uiPartEnd;
		}

		//while curl finish the transfer, there may be some buffer less than threashold which is not written to file
		//this method flushing these buffer to file
		void FlushingBufferToFile()
		{
			if(uiCurrentBufferedSize > 0)
			{
				DWORD	dwWritten;
				BOOL bResult;

				LARGE_INTEGER liTempFilePos;
				LARGE_INTEGER liCurrentPos;

				//if this written cause exceeded the part border, reduce written size
				//When run to here, part download is finished, so we need not to check finish
				if((uiFileWrittenPartSize + uiCurrentBufferedSize) > uiFileVirtualPartSize)
				{
					uiCurrentBufferedSize -= (uiFileWrittenPartSize + uiCurrentBufferedSize - uiFileVirtualPartSize);
				}

				//Write buffer to file, and reset the buffersize
				//cause in a multithread, so we shoud move the file pointer for our own part
				sm_csThreadWriteFile.Lock();

				liCurrentPos.QuadPart = lintPartStartPos.QuadPart + uiFileWrittenPartSize;
				bResult = ::SetFilePointerEx(hEntireFileHandle, liCurrentPos, &liTempFilePos, FILE_BEGIN);		
				::WriteFile(hEntireFileHandle, pcBuffer, uiCurrentBufferedSize, &dwWritten, NULL);

				sm_csThreadWriteFile.Unlock();

				uiFileWrittenPartSize += dwWritten;
				uiCurrentBufferedSize = 0;

				printf("\t Thread %d Write Data.....\n", sID);
			}
		}

	} PART_DOWNLOAD;

	//Data for a single thread in multithead download
	//It's a small struct for init thread part data, we just pass it to thread function
	//After that, this is useless
	typedef struct _PART_TASK_DESCRIPTOR {

		SHORT sPartID;
		UINT uiPartStartPos;
		UINT uiPartEndPos;

		DWORD dwThreadID;
		
		_PART_TASK_DESCRIPTOR() : sPartID(-1), uiPartStartPos(0), uiPartEndPos(0), dwThreadID(NULL) {}

	} PART_TASK_DESCRIPTOR;

	//Shared data for multithread download thread
	typedef struct _DOWN_FILE_DESCRIPTOR {

		//Downloading status
		typedef enum _DOWNLOADING_STATUS {
			ST_BEGINDOWNLOAD = 0,
			ST_TIMEOUT,
			ST_HTTPFAIL,
			ST_FINISH
		} DOWNLOADING_STATUS;
		
		HANDLE hFileHandle;
		string strFileLink;
		string strReferer;

		UINT uiTotalFileSize;

		BOOL bIsFTPLink;
		BOOL bIsPartDown;

		vector<PART_TASK_DESCRIPTOR> vecTaskParts;
		vector<PART_DOWNLOAD*> vecpPartDownInfo;

		_DOWN_FILE_DESCRIPTOR() : hFileHandle(NULL), uiTotalFileSize(0), bIsFTPLink(false), bIsPartDown(false) {}

		bool IsDownFinish()
		{	
			bool bRet = true;
			for(vector<PART_DOWNLOAD*>::iterator it = vecpPartDownInfo.begin(); it != vecpPartDownInfo.end(); ++it)
			{
				bRet = bRet && (*it)->bIsFinished;
			}

			return bRet;
		}

	} DOWN_FILE_DESCRIPTOR;

	//HTTP Header information, we need the struct above, so put it last
	typedef struct _HEADER_HTTP_INFO {
		LARGE_INTEGER liContentLength;
		string strLocation;			//If there is a redirection, save the location URL
		string strReqURL;			//Request URL which the head is reponsed for
		string strReferer;
		bool isRedirectFTP;
		bool isRedirectHTTP;
		bool isDirectHTTP;
		bool isHeaderProcessed;		//We do some job only onece in the header function, so we set this flag

		HANDLE	hPreCreatedFile;
		SHORT sUserSetThreads;

		CCURLHelper* pCurlHelper;
		DOWN_FILE_DESCRIPTOR* pDownFileDesc;	//We need this pointer to get information for all downloading thread

		_HEADER_HTTP_INFO() : isRedirectFTP(false), isRedirectHTTP(false), isDirectHTTP(false), isHeaderProcessed(false), hPreCreatedFile(NULL), pDownFileDesc(NULL), sUserSetThreads(1)
		{
			liContentLength.QuadPart = -1;
		}

		void ResetHttpStatus()
		{
			isRedirectFTP = false;
			isRedirectHTTP = false;
			isDirectHTTP = false;

			isHeaderProcessed = false;

			liContentLength.QuadPart = -1;
		}

	} HEADER_HTTP_INFO;

	//FTP header information, alike the http header
	typedef struct _HEADER_FTP_INFO {
		LARGE_INTEGER liFileSize;
		string strLocation;
		bool isResume;
		bool isError;
		bool isFileReady;

		HANDLE hPreCreatedFile;
		SHORT sUserSetThreads;
		SHORT sErrorType;

		CCURLHelper* pCurlHelper;
		DOWN_FILE_DESCRIPTOR* pDownFileDesc;

		//FTP Error
		enum {
			E_FTP_LOGIN = 0,
			E_FTP_NO_EXIST
		};

		_HEADER_FTP_INFO() : isResume(false), isFileReady(false), isError(false), sErrorType(-1), strLocation(""), hPreCreatedFile(NULL), sUserSetThreads(1), pCurlHelper(NULL), pDownFileDesc(NULL)
		{
			liFileSize.QuadPart = -1;
		}

	} HEADER_FTP_INFO;

	//Structs for items sniffer
	typedef struct _HTTP_ITEMS_SNIFFER {
		//We dislike to pass vector between thread, so we just pass the pointer
		vector<string>* pvecstrUrls;
		map<string, vector<CHttpStreamHandler::JSItem>>* pmapJSItems;
		map<string, string>* pmapJSSecIp;

		DWORD dwCallThreadID;

		//Sinffer status
		typedef enum _SNIFFER_STATUS {
			ST_STARTING = 0,
			ST_HTTPOK,
			ST_GETGZIP,
			ST_GETPLAIN,
			ST_DECODESTREAMOK,
			ST_DECODESTREAMERROR,
			ST_SNIFFERBEGIN,
			ST_SNIFFEROK,
			ST_SNIFFERZERO,
			ST_TIMEOUT,
			ST_HTTPFAIL,
			ST_OVERJOB
		} SNIFFER_STATUS;

		_HTTP_ITEMS_SNIFFER() : pvecstrUrls(NULL), pmapJSItems(NULL), dwCallThreadID(0) {}

	} HTTP_ITEMS_SNIFFER;

	typedef struct _PAGE_LINK_ITEM {

		//Inform the sniffer status
		typedef enum _LINK_STATUS {
			ST_BEGIN_CONNECT = 0,
			ST_STARTITEM,
			ST_HTTP_OK,
			ST_HTTP_GZIP,
			ST_HTTP_GZDECOK,
			ST_HTTP_GZDECFAIL,
			ST_HTTP_PLAIN,
			ST_NEED_CAPTCHA,
			ST_REC_CAPTCHA,
			ST_GETLINKOK,
			ST_GETLINKFAIL,
			ST_HTTPFAIL,
			ST_TIMEOUT,
			ST_WAITFORNEXT,
			ST_FINISH
		} LINK_STATUS;

		string strURLRefer;
		string strLinkPage;
		string strFileLink;
		string strDownLink;
		string strSecIp;
		string strCaptcha;
		wstring wstrFileID;
		wstring wstrServerFileName;
		wstring wstrPostFileName;

		//Save pointer to Iterator of Orgn Vector
		void* pListItem;
		bool bIsFoundLink;

		_PAGE_LINK_ITEM() : bIsFoundLink(false) {}

	} PAGE_LINK_ITEM;

	//Download file descriptor
	typedef struct _TASK_DESCRIPTOR {
		HANDLE hFile;

		wstring wstrFileFullPath;
		string strDownLoadLink;
		string strReferer;
		SHORT sUserSetDownThread;

		bool bIsFTPDownLoad;
		bool bIsTaskFinished;
		void* pListItem;

		union {
			HEADER_FTP_INFO* pFTPInfo;
			HEADER_HTTP_INFO* pHttpInfo;
		};

		_TASK_DESCRIPTOR() : sUserSetDownThread(1), hFile(NULL), bIsFTPDownLoad(false), bIsTaskFinished(false), pFTPInfo(NULL), pListItem(NULL) {}

	} TASK_DESCRIPTOR;

public:
	CCURLHelper() : m_curlHandle(NULL) { m_curlHandle = ::curl_easy_init(); }		//Init Curl Handle
	CCURLHelper(CURL* cHandle) : m_curlHandle(cHandle) { ATLASSERT(m_curlHandle != NULL); }		//Init with existing handle
	~CCURLHelper() { ::curl_easy_cleanup(m_curlHandle); }
	
	//Encapsulate Method, make them inline
	inline CURLcode DoEasyPerform();

	//HTTP
	inline CURLcode SetOptionReqContentEncoding(char* encode);
	inline CURLcode SetOptionReqClearEncoding();
	inline CURLcode SetOptionDisableAutoEncoding();
	inline CURLcode SetOptionEnableAutoEncoding();

	inline CURLcode SetOptionWriteFunction(_CURL_WRITE_DATA_FUNCTION pfun);
	inline CURLcode SetOptionWriteUserData(void* userp);

	inline CURLcode SetOptionHeaderFunction(_CURL_WRITE_DATA_FUNCTION pfun);
	inline CURLcode SetOptionHeaderUserData(void* userp);

	inline CURLcode SetOptionURL(const char* url);
	inline CURLcode SetOptionReqUserAgent(const char* ua);
	inline CURLcode SetOptionReqReferer(const char* ref);

	inline CURLcode SetOptionEnableAutoFollowReferer();
	inline CURLcode SetOptionDisableAutoFollowReferer();
	inline CURLcode SetOptionEnableFollowLocation();
	inline CURLcode SetOptionDisableFollowLocation();

	inline CURLcode SetOptionReqCookieString(char* cookie);
	inline CURLcode SetOptionReqCookieFile(const char* cookiefile);
	inline CURLcode SetOptionCookieJar(const char* cookiejar);

	inline CURLcode SetOptionConnectionTimeOut(LONG lSec);
	inline CURLcode SetOptionTransferTimeOut(LONG lSec);

	inline CURLcode GetOptionContentLength(UINT* puiContLength);
	inline CURLcode GetOptionEffectiveURL(string& strEffectURL);
	inline CURLcode GetDecodedURLFileName(const char* pcURL, wstring& wstrFileName);
	inline CURLcode EncodedPlainURL(string& strURL);
	inline CURLcode EncodedFTPLinkWidthPlainFileName(string& strURL);

	//FTP
	inline CURLcode SetOptionFTPNoCWD();		//This will make a better performance
	inline CURLcode SetOptionFTPSingleCWD();
	inline CURLcode SetOptionAdditionHeaderTestFTPResume();	//Make the curl send addtion header to ftp to confirm resume

	inline CURLcode SetOptionFTPNoEPSV();		//Do not Try PASV MODE for ipv6, disable EPSV if the ftp host is only on IPv4
	inline CURLcode SetOptionFTPTryEPSV();		//Try PASV MODE for ipv6, this is the default option

	//Multi Thread
	inline CURLcode SetOptionThreadNoSig();
	inline CURLcode SetOptionResumeFrom(UINT uiResumeFrom);

	//For easy param
	operator CURL*() { return m_curlHandle; }

public:
	inline static void InitCURLWin32();
	inline static void CleanCURLWin32();

	//Callbacks
	static size_t cb_write_stream_to_buffer(void* buffer, size_t size, size_t nmemb, void* userp);
	static size_t cb_write_header_to_vector(void* buffer, size_t size, size_t nmemb, void* userp);

	static size_t cb_http_header_router(void* buffer, size_t size, size_t nmemb, void* userp);
	static size_t cb_getinfo_ftp_header(void* buffer, size_t size, size_t nmemb, void* userp);
	static size_t cb_write_stream_to_file(void* buffer, size_t size, size_t nmemb, void* userp);
	static size_t cb_write_stream_part_file(void* buffer, size_t size, size_t nmemb, void* userp);

public:
	//Helper static funcion
	static UINT sm_uiDownBufferSize, sm_uiDownBufferThreshold;
	static const LARGE_INTEGER LINT_ZERO;
	static void ResetLargeInt(LARGE_INTEGER* lint) { (*lint).HighPart = 0; (*lint).LowPart = 0; }
	static void DivideVirtualFileIntoParts(SHORT sParts, DOWN_FILE_DESCRIPTOR* pDownFileDescriptor);
	static void GeneratePageLink(IJsSiteHelper* piJSHelper, PAGE_LINK_ITEM& itemDown, CHAR* pcCaptcha = NULL);

public:
	//Thread Wrapper
	static void __cdecl FtpPartDownloadThread(void* pParam);
	static void __cdecl StartHttpPartDownloadThread(void* pParam);
	static void __cdecl HttpPartDownloadThread(void* pParam);
	static void __cdecl HttpItemsSnifferThread(void* pParam);
	static void __cdecl PageLinkSnifferThread(void* pParam);
	static CComAutoCriticalSection sm_csTheadInit;
	static CComAutoCriticalSection sm_csThreadWriteFile;

public:
	CURL* m_curlHandle;
};

//Inline member function for better performance
inline void CCURLHelper::InitCURLWin32()
{
	::curl_global_init(CURL_GLOBAL_WIN32);
}

inline void CCURLHelper::CleanCURLWin32()
{
	::curl_global_cleanup();
}

inline CURLcode CCURLHelper::DoEasyPerform()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_perform(m_curlHandle);
}

inline CURLcode CCURLHelper::SetOptionWriteFunction(_CURL_WRITE_DATA_FUNCTION pfun)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_WRITEFUNCTION, pfun);
}

inline CURLcode CCURLHelper::SetOptionWriteUserData(void* userp)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_WRITEDATA, userp);
}

inline CURLcode CCURLHelper::SetOptionHeaderFunction(_CURL_WRITE_DATA_FUNCTION pfun)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_HEADERFUNCTION, pfun);
}

inline CURLcode CCURLHelper::SetOptionHeaderUserData(void* userp)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_WRITEHEADER, userp);
}

inline CURLcode CCURLHelper::SetOptionReqContentEncoding(char* encode)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_ENCODING, encode);
}

inline CURLcode CCURLHelper::SetOptionReqClearEncoding()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_ENCODING, "identity");
}

inline CURLcode CCURLHelper::SetOptionDisableAutoEncoding()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_HTTP_CONTENT_DECODING, 0L);
}

inline CURLcode CCURLHelper::SetOptionEnableAutoEncoding()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_HTTP_CONTENT_DECODING, 1L);
}

inline CURLcode CCURLHelper::SetOptionURL(const char* url)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_URL, url);
}

inline CURLcode CCURLHelper::SetOptionReqUserAgent(const char* ua)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_USERAGENT, ua);	
}

inline CURLcode CCURLHelper::SetOptionReqReferer(const char* ref)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_REFERER, ref);	
}

inline CURLcode CCURLHelper::SetOptionReqCookieString(char* cookie)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_COOKIE, cookie);
}

inline CURLcode CCURLHelper::SetOptionCookieJar(const char* cookiejar)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_COOKIEJAR, cookiejar);
}

inline CURLcode CCURLHelper::SetOptionReqCookieFile(const char* cookiefile)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_COOKIEFILE, cookiefile);
}

inline CURLcode CCURLHelper::SetOptionEnableAutoFollowReferer()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_AUTOREFERER, 1);
}

inline CURLcode CCURLHelper::SetOptionDisableAutoFollowReferer()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_AUTOREFERER, 0);
}

inline CURLcode CCURLHelper::SetOptionEnableFollowLocation()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_FOLLOWLOCATION, 1);
}

inline CURLcode CCURLHelper::SetOptionDisableFollowLocation()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_FOLLOWLOCATION, 0);
}

inline CURLcode CCURLHelper::SetOptionFTPNoCWD()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD);	
}

inline CURLcode CCURLHelper::SetOptionFTPSingleCWD()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD);
}

inline CURLcode CCURLHelper::SetOptionFTPNoEPSV()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_FTP_USE_EPSV, 0L);
}

inline CURLcode CCURLHelper::SetOptionFTPTryEPSV()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_FTP_USE_EPSV, 1L);
}

inline CURLcode CCURLHelper::SetOptionThreadNoSig()
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_NOSIGNAL, 1L);
}

inline CURLcode CCURLHelper::SetOptionResumeFrom(UINT uiResumeFrom)
{
	ATLASSERT(m_curlHandle != NULL);
	
	curl_off_t liResume = uiResumeFrom;

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_RESUME_FROM_LARGE, liResume);	
}

inline CURLcode CCURLHelper::SetOptionAdditionHeaderTestFTPResume()
{
	ATLASSERT(m_curlHandle != NULL);

	struct curl_slist *headers = NULL;

	headers = ::curl_slist_append(headers, "REST 100");
	headers = ::curl_slist_append(headers, "REST 0");
	
	return ::curl_easy_setopt(m_curlHandle, CURLOPT_QUOTE, headers);	
}

inline CURLcode CCURLHelper::GetOptionContentLength(UINT* puiContLength)
{
	ATLASSERT(m_curlHandle != NULL);

	DOUBLE dbContLength = 0.0;
	CURLcode cc = ::curl_easy_getinfo(m_curlHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &dbContLength);

	*puiContLength = (UINT)dbContLength;

	return cc;
}

inline CURLcode CCURLHelper::GetOptionEffectiveURL(string& strEffectURL)
{
	ATLASSERT(m_curlHandle != NULL);

	CHAR pcURL[_CURL_MAX_URL_LENGTH] = { 0 };
	CURLcode cc = ::curl_easy_getinfo(m_curlHandle, CURLINFO_EFFECTIVE_URL, &pcURL);

	strEffectURL = pcURL;

	return cc;
}

inline CURLcode CCURLHelper::SetOptionConnectionTimeOut(LONG lSec)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_CONNECTTIMEOUT, lSec);
}

inline CURLcode CCURLHelper::SetOptionTransferTimeOut(LONG lSec)
{
	ATLASSERT(m_curlHandle != NULL);

	return ::curl_easy_setopt(m_curlHandle, CURLOPT_TIMEOUT, lSec);
}

inline CURLcode CCURLHelper::GetDecodedURLFileName(const char* pcURL, wstring& wstrFileName)
{
	ATLASSERT(m_curlHandle != NULL);

	//Extract FielName part from url
	CHAR pcFileNameBuffer[MAX_PATH] = { 0 };
	WCHAR pwcFileName[MAX_PATH] = { 0 };
	string strFullURL = pcURL;
	string::size_type nPos = strFullURL.find_last_of('/');

	string strFileNamePart = strFullURL.substr(nPos+1);

	char* pcResult = ::curl_easy_unescape(m_curlHandle, strFileNamePart.c_str(), NULL, NULL);

	if(pcResult != NULL)
	{
		::strcpy(pcFileNameBuffer, pcResult);
		::curl_free(pcResult);

		//Convert MBCS to UNICODE, we assume the code page is GB2312
		::MultiByteToWideChar(936, MB_PRECOMPOSED, pcFileNameBuffer, -1, pwcFileName, MAX_PATH);
		wstrFileName = pwcFileName;

		return CURLE_OK;
	}

	return CURLE_URL_MALFORMAT;
}

inline CURLcode CCURLHelper::EncodedPlainURL(string& strURL)
{
	ATLASSERT(m_curlHandle != NULL);

	//Escape the url
	char* pcResult = ::curl_easy_escape(m_curlHandle, strURL.c_str(), NULL);

	if(pcResult != NULL)
	{
		strURL = pcResult;
		::curl_free(pcResult);

		return CURLE_OK;
	}

	return CURLE_URL_MALFORMAT;
}

inline CURLcode CCURLHelper::EncodedFTPLinkWidthPlainFileName(string& strURL)
{
	ATLASSERT(m_curlHandle != NULL);

	//Get the ftp filename part
	string::size_type nPos = strURL.find_last_of('/');
	string strFileNamePart = strURL.substr(nPos+1);
	string strFTPPathPart = strURL.substr(0, nPos+1);

	char* pcResult = ::curl_easy_escape(m_curlHandle, strFileNamePart.c_str(), NULL);

	if(pcResult != NULL)
	{
		strURL = strFTPPathPart;
		strURL += pcResult;
		::curl_free(pcResult);

		return CURLE_OK;
	}

	return CURLE_URL_MALFORMAT;
}